﻿using System;
using System.Collections.Generic;
using System.Windows;
#if WPF
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Media;
#else
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Shapes;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
#endif

namespace Eternal.DataVisualization
{
    public class YAxis : Axis
    {
        protected Canvas HeaderPanel { get; set; }

        public PositionInAreaY PositionInArea
        {
            get { return (PositionInAreaY)GetValue(PositionInAreaProperty); }
            set { SetValue(PositionInAreaProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PositionInArea.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PositionInAreaProperty =
            DependencyProperty.Register("PositionInArea", typeof(PositionInAreaY), typeof(YAxis), new PropertyMetadata(PositionInAreaY.Left));

        protected override void IterateData()
        {
            //if (AxisData != null)
            //{
            //    padding = true;
            //    IEnumerator enumerator = AxisData.GetEnumerator();
            //    object val = null;
            //    int i = 0;
            //    if (enumerator.MoveNext())
            //    {
            //        val = enumerator.Current;
            //        if (val is DateTime)
            //        {
            //            ConvertedData = new List<double>();
            //            ActualData = new List<DateTime>();
            //            do
            //            {
            //                DateTime currVal = (DateTime)enumerator.Current;
            //                ActualData.Add(enumerator.Current);
            //                ConvertedData.Add(currVal.ToOADate());
            //            } while (enumerator.MoveNext());
            //            Type = "DateTime";
            //        }
            //        else
            //        {
            //            ActualData = new List<double>();
            //            do
            //            {
            //                ActualData.Add(enumerator.Current);
            //                i++;
            //            } while (enumerator.MoveNext());
            //            Type = "Double";
            //        }
            //        UpdateMinimumMaximum();
            //        ActualDelta = ActualEnd - ActualStart;
            //        ActualDelta = ActualDelta == 0 ? 1 : ActualDelta;
            //        //Start = ActualStart;
            //        //End = ActualEnd;
            //    }
            //}
        }

        protected override void CalculateInterval()
        {
            if (Interval == null)
                ActualInterval = ActualDelta / 6;
        }

        protected override Size MeasureOverride(Size constraint)
        {
            if (!double.IsPositiveInfinity(constraint.Height))
            {
                if (constraint.Height != ActualSize.Height)
                    CanUpdate = true;
                ActualSize.Height = constraint.Height;
            }
            return base.MeasureOverride(ActualSize);
        }

        double maxWidth;
        protected override void GenerateRecycleElement()
        {
            HeaderPresenter.SetValue(Canvas.LeftProperty, -5d);
            //HeaderPresenter.SetValue(Canvas.TopProperty, ActualSize.Height/2);
            HeaderPanel.Width = HeaderPresenter.DesiredSize.Height;
            HeaderPanel.Height = HeaderPresenter.DesiredSize.Width;
            TickPresenter.Height = LabelPresenter.Height = ActualSize.Height;
            TickPresenter.Width = OuterTickSize;
            LabelPresenter.Width = maxWidth;
            var axislabelEnum = AxisLabelRecycler.RecycleChildren(Labels.Count).GetEnumerator();
            var tickElementEnum = TickRecycler.RecycleChildren(Labels.Count).GetEnumerator();
            Line line = null;
            double actualElementSize = 0;
            foreach (var label in Labels)
            {
                if (tickElementEnum.MoveNext())
                    line = tickElementEnum.Current;
                if (line == null) continue;
                line.Style = OuterTickStyle;
                ContentPresenter control = null;
                if (axislabelEnum.MoveNext())
                {
                    control = axislabelEnum.Current;
                    control.Content = null;
                    control.Content = label.Content;
                    label.Element = control;
                    label.Element.Measure(tempMeasureSize);
                    actualElementSize = control.DesiredSize.Height;
                }

                line.X2 = OuterTickSize;
                var position = label.Position;
                line.SetValue(Canvas.LeftProperty, position.X);
                line.SetValue(Canvas.TopProperty, position.Y);

                if (control == null) continue;
                control.SetValue(Canvas.LeftProperty, position.X + (maxWidth - control.DesiredSize.Width));
                control.SetValue(Canvas.TopProperty, position.Y - actualElementSize/2);
            }
            AxisLine.Y2 = ActualSize.Height;
        }

        public override Point GetPoint(Axis axis, double value)
        {
            return new Point(0,
                             Math.Round(axis.ActualSize.Height * (1 - (value - axis.Start) / axis.ActualDelta)));
        }

#if WPF
        public override void OnApplyTemplate()
#else
        protected override void OnApplyTemplate()
#endif
        {
            base.OnApplyTemplate();
            HeaderPanel = new Canvas();
            HeaderPresenter = new ContentPresenter();
            HeaderPanel.Children.Add(HeaderPresenter);
            Labels = new List<AxisLabel>();
            RootPanel = GetTemplateChild("PART_Root") as StackPanel;
            Presenters = new List<UIElement>();
            LabelPresenter = new Canvas();
            TickPresenter = new Canvas();
            SmallTickPresenter = new Canvas();
            AxisLine = new Line {Style = AxisLineStyle};
            HeaderPresenter.Content = Header;
            Presenters.Add(HeaderPanel);
            Presenters.Add(LabelPresenter);
            Presenters.Add(TickPresenter);
            //Presenters.Add(SmallTickPresenter);
            //Presenters.Add(AxisLine);
            AxisLabelRecycler = new ElementRecycler<ContentPresenter>(LabelPresenter);
            TickRecycler = new ElementRecycler<Line>(TickPresenter);
            if (RootPanel != null) RootPanel.Orientation = Orientation.Horizontal;
            //Presenters.Add(HeaderPresenter);
            //LabelPresenter.Children.Add(HeaderPresenter);
            HeaderPresenter.RenderTransform = new RotateTransform { Angle = 270 };
            HeaderPresenter.RenderTransformOrigin = new Point(0.5, 0.5);
            HeaderPresenter.VerticalAlignment = VerticalAlignment.Center;
            AddToVisual();
        }

        protected override void AddToVisual()
        {
            if (AxisPosition == Position.OutSideArea)
            {
                foreach (var item in Presenters)
                {
                    RootPanel.Children.Add(item);
                }
                RootPanel.Children.Add(AxisLine);
            }
            else
            {
                RootPanel.Children.Add(AxisLine);
                for (var i = Presenters.Count - 1; i >= 0; i--)
                {
                    RootPanel.Children.Add(Presenters[i]);
                }
               
            }
        }

        protected override void GenerateLabelsAndSize()
        {
            contentContrl.Content = null;
            contentContrl.ContentTemplate = null;
            maxWidth = 0;
            Labels.Clear();
            switch (Type)
            {
                case "Double":
                    GenerateDoubleLabels();
                    break;
                case "DateTime":
                    GenerateDateTimeLabels();
                    break;
            }


            AxisLine.Measure(tempMeasureSize);
            HeaderPresenter.Measure(tempMeasureSize);
            ActualSize.Width = HeaderPresenter.DesiredSize.Height + maxWidth + OuterTickSize + AxisLine.DesiredSize.Width;
        }

        void GenerateDoubleLabels()
        {
            for (var i = Start; i < End; i = i + ActualInterval)
            {
                var label = new AxisLabel {Position = GetPoint(this, i), ActualContent = i};

                //if (previousPos > label.Position.Y)
                //{
                //    continue;
                //}
                contentContrl.Content = label.Content = i.ToString("00.00");
                contentContrl.Measure(tempMeasureSize);
                Labels.Add(label);

                var actualValue = contentContrl.DesiredSize.Width;
                maxWidth = Math.Max(maxWidth, actualValue);
                //previousPos = label.Position.Y - contentContrl.DesiredSize.Width;
            }
        }

        internal override void UpdateMinimumMaximum()
        {

        }

        void GenerateDateTimeLabels()
        {
            var previousPos = ActualSize.Height;
            var j = 0;
            for (var i = (int)Start; i <= (int)End; i += (int)Math.Ceiling(ActualInterval))
            {
                var label = new AxisLabel
                {
                    Position = new Point(0,
                        Math.Round(ActualSize.Height*(1 - ((i - ActualStart)/ActualDelta))))
                };
                if (previousPos < label.Position.Y)
                {
                    j++;
                    continue;
                }
                label.ActualContent = i;
                contentContrl.Content = label.Content = ActualData[j];
                contentContrl.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                Labels.Add(label);

                var actualValue = contentContrl.DesiredSize.Width;
                maxWidth = maxWidth < actualValue ? actualValue : maxWidth;
                previousPos = label.Position.X + contentContrl.DesiredSize.Width;
                j++;
            }
        }

        protected override void ApplyPadding()
        {
            Start -= ActualInterval;
            End += ActualInterval;
            base.ApplyPadding();
        }
    }
}